/*
 * Copyright (c) 2002 David Corbin and others. All rights reserved. This program
 * and the accompanying materials are made available under the terms of the
 * Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: David Corbin - Add support for creating depot "connections" in
 * all cases Boris Pruessmann - Tweaked behavior
 */
package net.sourceforge.perforce.ui.wizards;

import com.google.common.collect.Lists;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IClientForm;
import net.sourceforge.perforce.core.api.IPerforceListener;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.internal.api.FolderRepositoryRelation;
import net.sourceforge.perforce.ui.IPerforceUIConstants;
import net.sourceforge.perforce.ui.PerforceUIPlugin;
import net.sourceforge.perforce.ui.internal.Policy;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import java.util.Collections;
import java.util.List;

/**
 * Allows users to connect a project to a perforce server.
 *
 * @version $Revision: 494 $
 * @author <a href="mailto:dcorbin@users.sourceforge.net">David Corbin</a>
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
class ConnectProjectPage extends PerforceWizardPage implements IPerforceListener {

  /** Combo box containing the servers. */
  private Combo serverCombo;

  private Button serverButton;

  /** Text field showing the project's location. */
  private Text location;

  /** Location Browse button */
  private Button locationButton;

  /** Label showing additional information. */
  private Label projectLocationWarning;

  /** Stores the current project path. */
  private IPath currentProjectPath;

  private final List<IPerforceServer> servers = Lists.newArrayList();

  /**
   * Constructor for ConnectProjectPage.
   *
   * @param projectLocation the project location.
   */
  ConnectProjectPage(IPath projectLocation) {
    super("mappingPage", Policy.bind("ConnectProjectPage.title"), //$NON-NLS-1$
        PerforceUIPlugin.getPlugin().getImageDescriptor(IPerforceUIConstants.IMG_WIZBAN_SHARE));

    this.currentProjectPath = projectLocation;
    Collections.addAll(servers, PerforceCorePlugin.getServerManager().getPerforceServers());
    super.setMessage(Policy.bind("ConnectProjectPage.subtitle"));
  }

  @Override
  public void setErrorMessage(String msg) {
    super.setErrorMessage(msg);
    setPageComplete(msg == null);
  }

  /**
   * Called to check whether the page allows finish or not.
   *
   * @return true if page can finish.
   */
  public boolean canFinish() {
    return getErrorMessage() == null;
  }

  public void createControl(Composite parent) {
    Composite composite = createComposite(parent, 3);

    // Perforce Server
    createLabel(composite, Policy.bind("ConnectProjectPage.perforceServer")); //$NON-NLS-1$
    serverCombo = createCombo(composite);
    GridData data = (GridData) serverCombo.getLayoutData();
    serverButton = createButton(composite, "New Server...");

    PerforceCorePlugin.getServerManager().addPerforceListener(this);


    // Project Location
    createLabel(composite, Policy.bind("ConnectProjectPage.projectLocation")); //$NON-NLS-1$
    location = createTextField(composite);
    location.setEditable(true);
    locationButton = createButton(composite, Policy.bind("ConnectProjectPage.browse")); //$NON-NLS-1$

    // Project Location Warning
    projectLocationWarning = createCenteredLabel(composite);
    data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL);
    projectLocationWarning.setLayoutData(data);
    data.horizontalSpan = 3;
    data.horizontalAlignment = GridData.CENTER;
    data.heightHint = 50;
    data.widthHint = 300;

    linkProjectLocationControls(parent);

    initializeValue();
    serverCombo.setFocus();

    setControl(composite);
  }

  /**
   * Call this method to retrieve the project's "new" location.
   *
   * @return the new location of the project.
   */
  public IPath getProjectLocation() {
    return new Path(location.getText());
  }

  /**
   * Call this method to retrieve the currently selected Perforce server.
   *
   * @return the selected Perforce Server.
   */
  public IPerforceServer getServer() {
    if (serverCombo.getSelectionIndex() >= 0) return servers.get(serverCombo.getSelectionIndex());
    return null;
  }

  /** Initializes the values of the controls. */
  private void initializeValue() {
    initializeServers(0);

    // Perforce Location
    location.setText(currentProjectPath.toOSString());
  }

  @Override
  public void dispose() {
    PerforceCorePlugin.getServerManager().removePerforceListener(this);
    super.dispose();
  }

  private void initializeServers(int selection) {
    // Perforce Server
    serverCombo.removeAll();
    for (int i = 0; i < servers.size(); i++) {
      serverCombo.add(servers.get(i).getUniqueName());
    }
    serverCombo.select(selection);
  }

  /**
   * Call this method to retrieve the client form of the currently selected
   * server.
   *
   * @return the client form.
   *
   * @throws PerforceException in case of an error.
   */
  private IClientForm getClientForm() throws PerforceException {
    return getServer().getClientForm();
  }

  /**
   * Called to adjust the values of the page's controls.
   *
   * @param pathName the current project path.
   */
  private void adjustControls(String pathName) {
    setErrorMessage(null); // give us a fresh slate

    try {
      if (serverCombo.getItems().length == 0) {
        setErrorMessage(Policy.bind("ConnectProjectPage.newServerRequired")); //$NON-NLS-1$
        return;
      }
      if (pathName.equals("")) {
        adjustControls(FolderRepositoryRelation.OUTSIDE_ROOT);
        return;
      }

      IPath path = new Path(pathName);
      IPath workspaceRoot = ResourcesPlugin.getWorkspace().getRoot().getLocation();

      projectLocationWarning.setText(moveMessageText(path));

      if (path.isPrefixOf(workspaceRoot)) {
        setErrorMessage(Policy.bind("ConnectProjectPage.cannotBeParent")); //$NON-NLS-1$
        return;
      }

      //adjustControls(getServer().determineFolderPosition(pathName));
    } catch (PerforceException e) {
      PerforceUIPlugin.log(e.getStatus());
    }
  }

  /**
   * Called to adjust the values of the page's controls based on the given
   * relation.
   *
   * @param newRelation the relation of the project path to the client spec.
   *
   * @throws PerforceException in case of an error.
   */
  private void adjustControls(FolderRepositoryRelation newRelation) throws PerforceException {

    if (newRelation == null) return; // not sure when this will be null

    setErrorMessage(null); // give us a fresh slate

    if (newRelation == FolderRepositoryRelation.IN_VIEW) {
      setErrorMessage(null);
    } else if (newRelation == FolderRepositoryRelation.IN_REPOSITORY) {
      setErrorMessage(null);
      projectLocationWarning.setText(Policy.bind("ConnectProjectPage.projectInDepot")); //$NON-NLS-1$
    } else if (newRelation == FolderRepositoryRelation.OUTSIDE_ROOT) {
      IClientForm form = getClientForm();
        setErrorMessage(Policy.bind("ConnectProjectPage.notBeneathRoot", //$NON-NLS-1$
            (form != null) ? form : Policy.bind("unknown")));
    } else if (newRelation == FolderRepositoryRelation.UNDER_ROOT) {
      // Do nothing
    } else {
      setErrorMessage(Policy.bind("ConnectProjectPage.unexpectedResponse")); //$NON-NLS-1$
    }
  }

  /**
   * Returns the text to be shown when the project is moved.
   *
   * @param path the new path.
   * @return String the text.
   */
  private String moveMessageText(IPath path) {
    if (path.equals(currentProjectPath)) {
      return ""; //$NON-NLS-1$
    }

    return Policy.bind("ConnectProjectPage.projectWillMove"); //$NON-NLS-1$
  }

  /**
   * Call this method to link the controls' behavior.
   *
   * @param parent the parent control.
   */
  private void linkProjectLocationControls(final Composite parent) {
    // Perforce Server
    serverCombo.addSelectionListener(new SelectionListener() {
      public void widgetDefaultSelected(SelectionEvent e) {
        widgetSelected(e);
      }

      public void widgetSelected(SelectionEvent e) {
        adjustControls(location.getText());
      }

    });

    serverButton.addSelectionListener(new SelectionListener() {

      public void widgetSelected(SelectionEvent e) {
        NewLocationWizard wizard = new NewLocationWizard(getProjectLocation());
        WizardDialog dialog = new WizardDialog(parent.getShell(), wizard);
        dialog.open();
      }

      public void widgetDefaultSelected(SelectionEvent e) {
        widgetSelected(e);
      }

    });

    // Project Location
    locationButton.addSelectionListener(new SelectionListener() {
      public void widgetDefaultSelected(SelectionEvent event) {
        widgetSelected(event);
      }

      public void widgetSelected(SelectionEvent event) {
        try {
          DirectoryDialog dialog = new DirectoryDialog(parent.getShell());
          IPath clientRoot = getServer().getClientRoot();
          dialog.setFilterPath((clientRoot != null) ? clientRoot.toOSString() :
              ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());

          String pathString = dialog.open();
          if (pathString != null) {
            IPath path = new Path(pathString).append(currentProjectPath.lastSegment());
            location.setText(path.toOSString());
          }
        } catch (PerforceException e) {
          PerforceUIPlugin.log(e.getStatus());
        }
      }
    });

    location.addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent event) {
        adjustControls(location.getText());
      }
    });
  }

  public void perforceServerAdded(IPerforceServer server) {
    serverCombo.add(server.getUniqueName());
    serverCombo.select(serverCombo.getItems().length - 1);
    servers.add(server);
    adjustControls(location.getText());
  }

  public void perforceServerRemoved(IPerforceServer server) {
  }
}
